use merge_yaml_hash::MergeYamlHash;
use std::collections::HashMap;
use std::fs;

/// Config
#[derive(Debug, PartialEq, serde::Serialize, serde::Deserialize)]
pub struct ApplicationConfig {
    pub debug: bool,
    pub server_url: String,
    pub base_api: String,
    pub db_url: String,
    pub db_pool_len: usize,
    pub db_pool_timeout: usize,
    pub log_dir: String,
    pub log_rolling: String,
    pub log_pack_compress: String,
    pub log_keep_type: String,
    pub log_level: String,
    pub log_chan_len: Option<usize>,
    pub sms_cache_send_key_prefix: String,
    pub jwt_secret: String,
    pub jwt_exp: usize,
    pub jwt_refresh_token: usize,

    pub captcha_expired_min: u64,
    pub token_expired_min: u64,
    pub address_enabled:bool,
    pub cache: String,
    pub storage: String,
    pub login_fail_retry: u64,
    pub login_fail_retry_wait_sec: u64,
    pub trash_recycle_days: u64,
    pub datetime_format: String,
    pub errors: HashMap<String, String>,
    pub error_infos: Option<HashMap<String, String>>
}

impl Default for ApplicationConfig {
    fn default() -> Self {
        let k = "profile=";
        let profile = std::env::args_os()
            .find(|v| v.to_str().unwrap_or_default().starts_with(k))
            .map(|v| v.to_str().unwrap_or_default().trim_start_matches(k).to_string());
        let profile = match profile {
            Some(o) => Some(o),
            None => {
                std::env::var("profile").ok()
            }
        };
        let profile = match profile {
            Some(o) => Some(o),
            None => {
                std::env::var_os("profile").map(|x| x.into_string().unwrap_or_default())
            }
        };
        let profile = profile.unwrap_or_else(|| "prod".to_string());
        let profile = profile.trim().to_string();
        println!("loading profile: {}", profile);
        let yml_data = include_str!("../../application.yml");

        let mut hash = MergeYamlHash::new();

        hash.merge(yml_data);
        if profile.len() > 0 {
            match fs::read_to_string(format!("application-{profile}.yml")) {
                Ok(s) => { hash.merge(&s); }
                Err(_) => { println!("Can't loading application-{profile}.yml"); }
            }
        }
        //load config
        let result: ApplicationConfig =
            serde_yaml::from_str(&*hash.to_string()).expect("load config file fail");
        if result.debug {
            //println!("[ruoyi_rust] load config:{:?}", result);
            println!("[ruoyi_rust] ///////////////////// Start On Debug Mode ////////////////////////////");
        } else {
            println!("[ruoyi_rust] ///////////////////// Start On Release Mode ////////////////////////////");
        }
        result
    }
}

impl ApplicationConfig {
    pub fn get_error_info(&self, code: &str) -> String {
        match self.errors.get(code) {
            None => match self.errors.get("-1") {
                None => "unknown error".to_string(),
                Some(v) => v.to_string(),
            },
            Some(v) => v.as_str().to_string(),
        }
    }

    pub fn init_infos(&mut self) {
        self.error_infos = Some(HashMap::new());
        for (k, error) in &self.errors {
            let mut error = error.to_string();
            if error.contains(",") {
                error = error[0..error.find(",").unwrap()].to_string();
            }
            self.error_infos
                .as_mut()
                .unwrap()
                .insert(error, k.to_string());
        }
    }
}

#[macro_export]
macro_rules! error_info {
    ($code: expr) => {
        $crate::context::CONTEXT.config.get_error_info($code)
    };
}
